/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.plc4x.java.bacnetip.readwrite;

import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
import static org.apache.plc4x.java.spi.generation.StaticHelper.*;

import java.time.*;
import java.util.*;
import org.apache.plc4x.java.api.exceptions.*;
import org.apache.plc4x.java.api.value.*;
import org.apache.plc4x.java.spi.codegen.*;
import org.apache.plc4x.java.spi.codegen.fields.*;
import org.apache.plc4x.java.spi.codegen.io.*;
import org.apache.plc4x.java.spi.generation.*;

// Code generated by code-generation. DO NOT EDIT.

public class NPDU implements Message {

  // Properties.
  protected final short protocolVersionNumber;
  protected final NPDUControl control;
  protected final Integer destinationNetworkAddress;
  protected final Short destinationLength;
  protected final List<Short> destinationAddress;
  protected final Integer sourceNetworkAddress;
  protected final Short sourceLength;
  protected final List<Short> sourceAddress;
  protected final Short hopCount;
  protected final NLM nlm;
  protected final APDU apdu;

  public NPDU(
      short protocolVersionNumber,
      NPDUControl control,
      Integer destinationNetworkAddress,
      Short destinationLength,
      List<Short> destinationAddress,
      Integer sourceNetworkAddress,
      Short sourceLength,
      List<Short> sourceAddress,
      Short hopCount,
      NLM nlm,
      APDU apdu) {
    super();
    this.protocolVersionNumber = protocolVersionNumber;
    this.control = control;
    this.destinationNetworkAddress = destinationNetworkAddress;
    this.destinationLength = destinationLength;
    this.destinationAddress = destinationAddress;
    this.sourceNetworkAddress = sourceNetworkAddress;
    this.sourceLength = sourceLength;
    this.sourceAddress = sourceAddress;
    this.hopCount = hopCount;
    this.nlm = nlm;
    this.apdu = apdu;
  }

  public short getProtocolVersionNumber() {
    return protocolVersionNumber;
  }

  public NPDUControl getControl() {
    return control;
  }

  public Integer getDestinationNetworkAddress() {
    return destinationNetworkAddress;
  }

  public Short getDestinationLength() {
    return destinationLength;
  }

  public List<Short> getDestinationAddress() {
    return destinationAddress;
  }

  public Integer getSourceNetworkAddress() {
    return sourceNetworkAddress;
  }

  public Short getSourceLength() {
    return sourceLength;
  }

  public List<Short> getSourceAddress() {
    return sourceAddress;
  }

  public Short getHopCount() {
    return hopCount;
  }

  public NLM getNlm() {
    return nlm;
  }

  public APDU getApdu() {
    return apdu;
  }

  /** (destinationNetworkAddress(16bit) + destinationLength(8bit) + destinationLength)? */
  public int getDestinationLengthAddon() {
    return (int)
        (((getControl().getDestinationSpecified()) ? ((3) + (getDestinationLength())) : 0));
  }

  /** (sourceNetworkAddress(16bit) + sourceLength(8bit) + sourceLength)? */
  public int getSourceLengthAddon() {
    return (int) (((getControl().getSourceSpecified()) ? ((3) + (getSourceLength())) : 0));
  }

  /**
   * protocolVersionNumber(8bit) + control(8bit) + sourceLengthAddon + destinationLengthAddon +
   * hopcount
   */
  public int getPayloadSubtraction() {
    return (int)
        ((2)
            + ((((getSourceLengthAddon()) + (getDestinationLengthAddon()))
                + (((((getControl().getDestinationSpecified())) ? 1 : 0))))));
  }

  public void serialize(WriteBuffer writeBuffer) throws SerializationException {
    PositionAware positionAware = writeBuffer;
    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
    writeBuffer.pushContext("NPDU");

    // Simple Field (protocolVersionNumber)
    writeSimpleField(
        "protocolVersionNumber", protocolVersionNumber, writeUnsignedShort(writeBuffer, 8));

    // Simple Field (control)
    writeSimpleField("control", control, writeComplex(writeBuffer));

    // Optional Field (destinationNetworkAddress) (Can be skipped, if the value is null)
    writeOptionalField(
        "destinationNetworkAddress", destinationNetworkAddress, writeUnsignedInt(writeBuffer, 16));

    // Optional Field (destinationLength) (Can be skipped, if the value is null)
    writeOptionalField("destinationLength", destinationLength, writeUnsignedShort(writeBuffer, 8));

    // Array Field (destinationAddress)
    writeSimpleTypeArrayField(
        "destinationAddress", destinationAddress, writeUnsignedShort(writeBuffer, 8));

    // Virtual field (doesn't serialize anything, just makes the value available)
    int destinationLengthAddon = getDestinationLengthAddon();
    writeBuffer.writeVirtual("destinationLengthAddon", destinationLengthAddon);

    // Optional Field (sourceNetworkAddress) (Can be skipped, if the value is null)
    writeOptionalField(
        "sourceNetworkAddress", sourceNetworkAddress, writeUnsignedInt(writeBuffer, 16));

    // Optional Field (sourceLength) (Can be skipped, if the value is null)
    writeOptionalField("sourceLength", sourceLength, writeUnsignedShort(writeBuffer, 8));

    // Array Field (sourceAddress)
    writeSimpleTypeArrayField("sourceAddress", sourceAddress, writeUnsignedShort(writeBuffer, 8));

    // Virtual field (doesn't serialize anything, just makes the value available)
    int sourceLengthAddon = getSourceLengthAddon();
    writeBuffer.writeVirtual("sourceLengthAddon", sourceLengthAddon);

    // Optional Field (hopCount) (Can be skipped, if the value is null)
    writeOptionalField("hopCount", hopCount, writeUnsignedShort(writeBuffer, 8));

    // Virtual field (doesn't serialize anything, just makes the value available)
    int payloadSubtraction = getPayloadSubtraction();
    writeBuffer.writeVirtual("payloadSubtraction", payloadSubtraction);

    // Optional Field (nlm) (Can be skipped, if the value is null)
    writeOptionalField("nlm", nlm, writeComplex(writeBuffer));

    // Optional Field (apdu) (Can be skipped, if the value is null)
    writeOptionalField("apdu", apdu, writeComplex(writeBuffer));

    writeBuffer.popContext("NPDU");
  }

  @Override
  public int getLengthInBytes() {
    return (int) Math.ceil((float) getLengthInBits() / 8.0);
  }

  @Override
  public int getLengthInBits() {
    int lengthInBits = 0;
    NPDU _value = this;
    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();

    // Simple field (protocolVersionNumber)
    lengthInBits += 8;

    // Simple field (control)
    lengthInBits += control.getLengthInBits();

    // Optional Field (destinationNetworkAddress)
    if (destinationNetworkAddress != null) {
      lengthInBits += 16;
    }

    // Optional Field (destinationLength)
    if (destinationLength != null) {
      lengthInBits += 8;
    }

    // Array field
    if (destinationAddress != null) {
      lengthInBits += 8 * destinationAddress.size();
    }

    // A virtual field doesn't have any in- or output.

    // Optional Field (sourceNetworkAddress)
    if (sourceNetworkAddress != null) {
      lengthInBits += 16;
    }

    // Optional Field (sourceLength)
    if (sourceLength != null) {
      lengthInBits += 8;
    }

    // Array field
    if (sourceAddress != null) {
      lengthInBits += 8 * sourceAddress.size();
    }

    // A virtual field doesn't have any in- or output.

    // Optional Field (hopCount)
    if (hopCount != null) {
      lengthInBits += 8;
    }

    // A virtual field doesn't have any in- or output.

    // Optional Field (nlm)
    if (nlm != null) {
      lengthInBits += nlm.getLengthInBits();
    }

    // Optional Field (apdu)
    if (apdu != null) {
      lengthInBits += apdu.getLengthInBits();
    }

    return lengthInBits;
  }

  public static NPDU staticParse(ReadBuffer readBuffer, Integer npduLength) throws ParseException {
    readBuffer.pullContext("NPDU");
    PositionAware positionAware = readBuffer;
    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();

    short protocolVersionNumber =
        readSimpleField("protocolVersionNumber", readUnsignedShort(readBuffer, 8));

    NPDUControl control =
        readSimpleField(
            "control", readComplex(() -> NPDUControl.staticParse(readBuffer), readBuffer));

    Integer destinationNetworkAddress =
        readOptionalField(
            "destinationNetworkAddress",
            readUnsignedInt(readBuffer, 16),
            control.getDestinationSpecified());

    Short destinationLength =
        readOptionalField(
            "destinationLength",
            readUnsignedShort(readBuffer, 8),
            control.getDestinationSpecified());
    // Validation
    if (!(((((control.getDestinationSpecified()) && ((destinationNetworkAddress) != (null)))
            && ((destinationLength) != (null))))
        || ((((!(control.getDestinationSpecified())) && ((destinationNetworkAddress) == (null)))
            && ((destinationLength) == (null)))))) {
      throw new ParseValidationException("inconsistent control");
    }

    List<Short> destinationAddress =
        readCountArrayField(
            "destinationAddress",
            readUnsignedShort(readBuffer, 8),
            ((control.getDestinationSpecified()) ? destinationLength : 0));
    int destinationLengthAddon =
        readVirtualField(
            "destinationLengthAddon",
            int.class,
            ((control.getDestinationSpecified()) ? ((3) + (destinationLength)) : 0));

    Integer sourceNetworkAddress =
        readOptionalField(
            "sourceNetworkAddress", readUnsignedInt(readBuffer, 16), control.getSourceSpecified());

    Short sourceLength =
        readOptionalField(
            "sourceLength", readUnsignedShort(readBuffer, 8), control.getSourceSpecified());
    // Validation
    if (!(((((control.getSourceSpecified()) && ((sourceNetworkAddress) != (null)))
            && ((sourceLength) != (null))))
        || ((((!(control.getSourceSpecified())) && ((sourceNetworkAddress) == (null)))
            && ((sourceLength) == (null)))))) {
      throw new ParseValidationException("inconsistent control");
    }

    List<Short> sourceAddress =
        readCountArrayField(
            "sourceAddress",
            readUnsignedShort(readBuffer, 8),
            ((control.getSourceSpecified()) ? sourceLength : 0));
    int sourceLengthAddon =
        readVirtualField(
            "sourceLengthAddon",
            int.class,
            ((control.getSourceSpecified()) ? ((3) + (sourceLength)) : 0));

    Short hopCount =
        readOptionalField(
            "hopCount", readUnsignedShort(readBuffer, 8), control.getDestinationSpecified());
    int payloadSubtraction =
        readVirtualField(
            "payloadSubtraction",
            int.class,
            (2)
                + ((((sourceLengthAddon) + (destinationLengthAddon))
                    + (((((control.getDestinationSpecified())) ? 1 : 0))))));

    NLM nlm =
        readOptionalField(
            "nlm",
            readComplex(
                () -> NLM.staticParse(readBuffer, (int) ((npduLength) - (payloadSubtraction))),
                readBuffer),
            control.getMessageTypeFieldPresent());

    APDU apdu =
        readOptionalField(
            "apdu",
            readComplex(
                () -> APDU.staticParse(readBuffer, (int) ((npduLength) - (payloadSubtraction))),
                readBuffer),
            !(control.getMessageTypeFieldPresent()));
    // Validation
    if (!(((nlm) != (null)) || ((apdu) != (null)))) {
      throw new ParseValidationException("something is wrong here... apdu and nlm not set");
    }

    readBuffer.closeContext("NPDU");
    // Create the instance
    NPDU _nPDU;
    _nPDU =
        new NPDU(
            protocolVersionNumber,
            control,
            destinationNetworkAddress,
            destinationLength,
            destinationAddress,
            sourceNetworkAddress,
            sourceLength,
            sourceAddress,
            hopCount,
            nlm,
            apdu);
    return _nPDU;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (!(o instanceof NPDU)) {
      return false;
    }
    NPDU that = (NPDU) o;
    return (getProtocolVersionNumber() == that.getProtocolVersionNumber())
        && (getControl() == that.getControl())
        && (getDestinationNetworkAddress() == that.getDestinationNetworkAddress())
        && (getDestinationLength() == that.getDestinationLength())
        && (getDestinationAddress() == that.getDestinationAddress())
        && (getSourceNetworkAddress() == that.getSourceNetworkAddress())
        && (getSourceLength() == that.getSourceLength())
        && (getSourceAddress() == that.getSourceAddress())
        && (getHopCount() == that.getHopCount())
        && (getNlm() == that.getNlm())
        && (getApdu() == that.getApdu())
        && true;
  }

  @Override
  public int hashCode() {
    return Objects.hash(
        getProtocolVersionNumber(),
        getControl(),
        getDestinationNetworkAddress(),
        getDestinationLength(),
        getDestinationAddress(),
        getSourceNetworkAddress(),
        getSourceLength(),
        getSourceAddress(),
        getHopCount(),
        getNlm(),
        getApdu());
  }

  @Override
  public String toString() {
    WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, true);
    try {
      writeBufferBoxBased.writeSerializable(this);
    } catch (SerializationException e) {
      throw new RuntimeException(e);
    }
    return "\n" + writeBufferBoxBased.getBox().toString() + "\n";
  }
}
